<?php

namespace Xin\Process\Python;

use ReflectionClass;
use Symfony\Component\Process\Process;

class Python extends Process
{
	use ParseProgressBar;

	/**
	 * @var Conda
	 */
	protected $conda;

	/**
	 * @var string
	 */
	protected $condaEnvName;

	/**
	 * @var string
	 */
	protected $pythonPath;

	/**
	 * @var string
	 */
	protected static $defaultPythonPath = "python";

	/**
	 * @var callable[]
	 */
	protected $startingCallbacks = [];

	/**
	 * @param array $command The command to run and its arguments listed as separate entries
	 * @param string|null $cwd The working directory or null to use the working dir of the current PHP process
	 * @param array|null $env The environment variables or null to use the same environment as the current PHP process
	 * @param mixed $input The input as stream resource, scalar or \Traversable, or null for no input
	 * @param float|null $timeout
	 */
	public function __construct(array $command, ?string $cwd = null, ?array $env = null, $input = null, ?float $timeout = 60)
	{
		parent::__construct($command, $cwd, $env, $input, null);

		$this->conda = Conda::getInstance();

		$this->pythonPath = static::$defaultPythonPath;

		$this->setIdleTimeout(60);
	}

	/**
	 * @return Conda|null
	 */
	public function getConda(): ?Conda
	{
		return $this->conda;
	}

	/**
	 * @param Conda $conda
	 * @return $this
	 */
	public function setConda(Conda $conda): Python
	{
		$this->conda = $conda;
		return $this;
	}

	/**
	 * @return string
	 */
	public function getCondaEnvName(): string
	{
		return $this->condaEnvName;
	}

	/**
	 * @param string $envName
	 * @return static
	 */
	public function useCondaEnv(string $envName): Python
	{
		$this->condaEnvName = $envName;

		return $this;
	}

	/**
	 * @inerhitDoc
	 */
	public function start(?callable $callback = null, array $env = [])
	{
		$commandline = $this->pythonPath . " " . $this->getCommandLine();

		if ($this->conda && $this->condaEnvName) {
			$condaBinPath = $this->conda->getCondaPath();
			$finalCommandline = "$condaBinPath run --no-capture-output -n {$this->condaEnvName} $commandline";
		} else {
			$finalCommandline = $commandline;
		}

		$reflection = new ReflectionClass($this);
		$parentReflection = $reflection->getParentClass();
		$propertyReflection = $parentReflection->getProperty('commandline');
		$propertyReflection->setAccessible(true);
		$propertyReflection->setValue($this, $finalCommandline);

		// 回调启动中的事件
		foreach ($this->startingCallbacks as $startingCallback) {
			$startingCallback($this);
		}

		parent::start($callback, $env);
	}

	/**
	 * @return string
	 */
	public function getPythonPath(): string
	{
		return $this->pythonPath;
	}

	/**
	 * @param string $pythonPath
	 * @return void
	 */
	public function setPythonPath(string $pythonPath): void
	{
		$this->pythonPath = $pythonPath;
	}

	/**
	 * @return string
	 */
	public static function getDefaultPythonPath(): string
	{
		return self::$defaultPythonPath;
	}

	/**
	 * @param string $defaultPythonPath
	 * @return void
	 */
	public static function setDefaultPythonPath(string $defaultPythonPath): void
	{
		self::$defaultPythonPath = $defaultPythonPath;
	}

	/**
	 * 绑定启动中的事件
	 * @param callable $callback
	 * @return $this
	 */
	public function starting(callable $callback)
	{
		$this->startingCallbacks[] = $callback;

		return $this;
	}
}
